/*
MIT License

Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,

https://bmstu.codes/lsx/simodo/stars
*/

#include "simodo/module/ModuleCollector.h"
#include "simodo/module/HardModuleLoader.h"
#include "simodo/inout/convert/functions.h"
#include "simodo/inout/format/fmt.h"

#include <filesystem>

using namespace simodo;
using namespace simodo::module;

namespace fs = std::filesystem;

ModuleCollector::ModuleCollector(std::vector<std::string> module_places)
    : _module_places(module_places)
{}

std::shared_ptr<variable::Array> ModuleCollector::produceObjectsByMask(const std::string & module_mask)
{
    std::shared_ptr<variable::Array> array = std::make_shared<variable::Array>();

    for(fs::path path : _module_places) {
        if (!fs::exists(path))
            continue;
        for (auto const & dir_entry : fs::directory_iterator{path})
            if (dir_entry.path().extension() == ".simodo-module") {
                std::string module_name = dir_entry.path().stem().string();
                if (module_name.substr(0,module_mask.size()) == module_mask) // -V1051
                {
                    std::shared_ptr<variable::Object> object = produceObject(module_name);
                    array->values().push_back(object);
                }
            }
    }

    return array;
}

std::shared_ptr<variable::Object> ModuleCollector::produceObject(const std::string & module_name,
                    interpret::Interpret_interface * interpret)
{
    _last_error = "";

    auto it = _modules.find(module_name);
    if (it != _modules.end())
        return std::make_shared<variable::Object>(it->second->instantiate(it->second));
    
    std::shared_ptr<variable::Module_interface> module_object = HardModuleLoader(_module_places).load(module_name,interpret);
    if (!module_object) {
        _last_error = inout::fmt("Unable to create module '%1'").arg(module_name);
        return {};
    }

    version_t v = module_object->version();
    if (v.major() != LibVersion_Major || v.minor() > LibVersion_Minor) {
        _last_error = inout::fmt("Incorrect version of module '%1'").arg(module_name);
        return {};
    }

    _modules.insert({module_name, module_object});
    return std::make_shared<variable::Object>(module_object->instantiate(module_object));
}

variable::Value ModuleCollector::invoke(variable::Object & object, const std::string & method_name, const variable::VariableSet_t & arguments)
{
    _last_error = "";

    const variable::Variable & function = object.getVariableByName(inout::toU16(method_name));
    if (function.type() == variable::ValueType::Null) {
        _last_error = inout::fmt("Member '%1' not found").arg(method_name);
        return {};
    }

    if (function.type() != variable::ValueType::Function) {
        _last_error = inout::fmt("Member '%1' is not a function").arg(method_name);
        return {};
    }

    return object.invoke(inout::toU16(method_name), arguments);
}
